#include "imageloader.h"
#include <QFileInfo>
#include <libraw/libraw.h>
#include <xmp.h>

#define NS_TIFF "http://ns.adobe.com/tiff/1.0/"

ImageLoader::ImageLoader()
    : m_cancel(false)
{
    QList<QByteArray> formats = QImageReader::supportedImageFormats();
    foreach(QByteArray ar, formats)
        m_supportedFormats.insert(QString(ar).toLower());
    QStringList raw_formats = QStringList() << "dng" << "crw" << "cr2" << "raw" << "erf" << "raf" << "3fr" << "dcr" << "dcs" << "kdc" << "raw" << "dng"
                         << "rwl" << "mef" << "mrw" << "mdc" << "nef" << "nrw" << "orf" << "raw" << "rw2" << "pef" << "dng" << "x3f"
                         << "cs1" << "cs4" << "cs16" << "srf" << "sr2" << "arw";
    foreach(QString fmt, raw_formats)
        m_supportedFormats.insert(fmt.toLower());
}

QImage ImageLoader::load(const QString & filename, const QSize & size)
{
    if(filename.isEmpty())// || size.isEmpty())
        return QImage();

    QStringList raw_formats = QStringList() << "dng" << "crw" << "cr2" << "raw" << "erf" << "raf" << "3fr" << "dcr" << "dcs" << "kdc" << "raw" << "dng"
                         << "rwl" << "mef" << "mrw" << "mdc" << "nef" << "nrw" << "orf" << "raw" << "rw2" << "pef" << "dng" << "x3f"
                         << "cs1" << "cs4" << "cs16" << "srf" << "sr2" << "arw";

    QFileInfo fi(filename);
    if(raw_formats.contains(fi.suffix().toLower()))
        return loadRaw(filename, size);
    else
        return loadNative(filename, size);
}

QImage ImageLoader::loadRaw(const QString & filename, const QSize & size)
{
    QImage image;
    libraw_data_t * lr = libraw_init(0);
    if(lr) {
        int r = libraw_open_file(lr, filename.toStdString().c_str());
        if(r == 0) {
            r = libraw_unpack_thumb(lr);
            if(r == 0) {
                if(lr->thumbnail.tformat == LIBRAW_THUMBNAIL_JPEG) {
                    image.loadFromData((unsigned char *)lr->thumbnail.thumb, lr->thumbnail.tlength);
                    int o = 0;
                    switch(lr->sizes.flip) {
                    case 3: o = 2; break;
                    case 5: o = 3; break;
                    case 6: o = 1; break;
                    }
                    scale(image, size, o);
                }
            }
        }
        libraw_close(lr);
    }
    return image;
}

QImage ImageLoader::loadNative(const QString & filename, const QSize & size)
{
    int o = orientation(filename);
    QImageReader reader(filename);
    if(!size.isNull()) {
        QSize tmp = size;
        if(o & 1)
            tmp.transpose();
        QSize size = reader.size();
        size.scale(tmp, Qt::KeepAspectRatio);
        reader.setScaledSize(size);
    }
    QImage image = reader.read();
    scale(image, size, o);
    return image;
}

void ImageLoader::scale(QImage &image, const QSize &size, int orientation)
{
    if(image.isNull() || size.isEmpty() || (image.size() == size && orientation == 0))
        return;

    QSize tmp = size;
    if(orientation & 1)
        tmp = QSize(size.height(), size.width());

    if(image.width() > 2 * tmp.width() || image.height() > 2 * tmp.height())
        image = image.scaled(2 * tmp.width(), 2 * tmp.height(), Qt::KeepAspectRatio, Qt::FastTransformation);
    image = image.scaled(tmp, Qt::KeepAspectRatio, Qt::SmoothTransformation);

    if(orientation) {
        QTransform transform;
        transform.rotate(90 * orientation);
        image = image.transformed(transform);
    }
}

int ImageLoader::orientation(const QString & filename)
{
    int orientation = 0;
    xmp_file_t * xmp = xmp_file_open(filename.toStdString().c_str());
    if(xmp) {
        const char * str = xmp_file_get_property(xmp, NS_TIFF, "Orientation");
        if(str != NULL) {
            switch(atoi(str)) {
            case 1: orientation = 0; break;
            case 6: orientation = 1; break;
            case 3: orientation = 2; break;
            case 8: orientation = 3; break;
            }
        }
        xmp_file_close(xmp);
    }
    return orientation;
}


bool ImageLoader::supports(const QString &suffix)
{
    return m_supportedFormats.contains(suffix);
}
